// **************************************************************
// Com ++ Framework by the C++ World
// Please report defects: mailto:compp@cplusplusworld.com
// Please visit: http://www.cplusplusworld.com/socketlibrary.html
// **************************************************************

#include <arpa/inet.h>
#include "ComPlusPlus/SSrv.h"

#include "SysPlusPlus/Tools.h"
#include "SysPlusPlus/Logger.h"

#include "Sig.h"

compp::SSrv::~SSrv () {
 Server.Shutdown();
}
compp::SSrv::SSrv () {

}

compp::SSrv::SSrv (const int p) {

  setPort ( p );
  setIPAddr ( "0.0.0.0" );

}

compp::SSrv::SSrv (const std::string & IPAddr, const int p) {

  setPort ( p );
  setIPAddr ( IPAddr );

}

compp::SSrv::SSrv (const std::string & IPAddr, const int p, const SSrvProperties & prop) {
  setPort ( p );
  setIPAddr ( IPAddr );
  ServerProperties = prop;
}

void compp::SSrv::SetProperties ( const SSrvProperties & prop) {
  ServerProperties = prop;
}

void compp::SSrv::commenceWork () {

  syspp::Logger *Log = syspp::Logger::Instance();

  Log->Info ("SSR Instance commencing main loop.");
  Server.Bind (IPAddr, port) ;
  Log->Info ("SSR Instance listening on %s:%d.", IPAddr.c_str(), port);
  Server.Listen ();
  Log->Info ("SSR Instance listening on %s:%d.", IPAddr.c_str(), port);

  Server.AttachFD();
  Server.SetSockOptReuseAddress ( true);
  Server.SetSockOptTcpNoDelay ( true ) ;
  Server.SetReadTimeout(ServerProperties.ReadTimeout);
  Server.SetWriteTimeout(ServerProperties.WriteTimeout);
  Server.SetLingerTime(0);

  Log->Info ("SSR Instance ReadTimeout=%d Writetimeout=%d",ServerProperties.ReadTimeout,ServerProperties.WriteTimeout);

  compp::SocketTcp *srv1;

  while ( true  ) {

    if ( NULL != ( srv1 = Server.Accept( ) )) {
      Log->Info ("SSR Instance received new connection.");
      this->Start ( (void *) srv1 );
      this->Detach();

    } else {
      std::string msg = "Server Accept failed: ";
      msg += strerror ( errno );
      Log->Info ( msg.c_str() );
      throw syspp::ComException ( msg.c_str() );
    }
  }
}

void *compp::SSrv::Run (void*v) {

  syspp::Logger *Log = syspp::Logger::Instance();

  compp::SocketTcp  * worker = (compp::SocketTcp  *) v;
  if ( worker == NULL ) {
    Log->Error ("SSR Instance not a TCP Socket");
    return NULL;
  }

  int LRequest, LSEC;
  std::string retval, request;

  SSrvContext Context;

  Context.SetAuth(false);
  Context.SetContextString ( worker->GetContextString());
  Log->Error ("SSR Instance received connection from %s:%d", worker->GetSocketAddress().c_str(), worker->GetSocketPort());
  try {
    do {
      int i ;
      if ( ( i = worker->Readn ( (char *) & LSEC, sizeof ( LSEC ))) <= 0  )  {
	worker->Shutdown ();
	worker->Close();
	delete worker;
	if ( i < 0 ) {
	 Log->Info ("SSR Instance. Read failed. dropping. (%s)", strerror ( errno ) );
	} else {
	 Log->Info ("SSR Instance. Client closed connection." );
	}
	return NULL;
      }
      
      LSEC =  ntohl ( LSEC );

      if ( LSEC != _COMM_SEC && LSEC != _COMM_CYPH ) {
        worker->Shutdown ();
        worker->Close();
        delete worker;
	Log->Info ("SSR Instance. Signature not valid. dropping." );
        return NULL;
      }

      if ( worker->Readn ( (char *) & LRequest, sizeof ( LRequest )) <= 0  )  {
	worker->Shutdown ();
	worker->Close();
	delete worker;
	Log->Info ("SSR Instance. Packet malformed. dropping.(%s)", strerror ( errno ) );
	return NULL;
      }
       LRequest = ntohl ( LRequest );
      if ( LRequest <= 0 ) {
	worker->Shutdown ();
        worker->Close();
        delete worker;
	Log->Info ("SSR Instance. Packet malformed. dropping." );
        return NULL;
      }
	
      if ( worker->Readn ( request, LRequest ) < 0 ) {
	worker->Shutdown ();
	worker->Close();
	Log->Info ("SSR Instance. Read failed (2). dropping. (%s)", strerror ( errno ) );
	delete worker;
      }

      if ( LSEC == _COMM_CYPH) {
//       std::cout << " Request cyphered " << request <<"\n";
	Log->Debug ("SSR Instance. Cyphering." );
	syspp::Tools::Dec (request);
      }

//       std::cout << "Received Request " << request <<"\n";
      retval = executeTransaction  ( request, Context  )  ;
      
      Log->Debug ("SSR Instance. Request executed. Response of length %d received.", retval.size() );

      if ( LSEC == _COMM_CYPH) {
	Log->Debug ("SSR Instance. Decypering." );
	syspp::Tools::Enc (retval);
      }


      int Len = htonl ( retval.size() );
      char c[sizeof (int)]; // Temporary
      memcpy ( (void*) c, (void*) &Len, sizeof (Len));

      std::string R = "";
      R.append ( c, sizeof ( Len ) );
      R.append ( retval );
      if ( worker->Writen ( R) < 0 ) {
	worker->Shutdown ();
	worker->Close();
	Log->Info ("SSR Instance. Cannot send response. (%s)", strerror ( errno ) );
	delete worker;
      }
      
      Log->Debug ("SSR Instance. Response sent." );


   } while ( true ) ;
  } catch ( syspp::ComException & e ) {
        worker->Shutdown ();
        worker->Close();
        delete worker;
	Log->Info ("SSR Instance. Received ComException: %s (%s)", e.what (), strerror ( errno ) );
        std::cout << "Abnormal Client Termination. " << e.what () << "\n";
  } catch ( ... ) {
        worker->Shutdown ();
        worker->Close();
        delete worker;
	Log->Info ("SSR Instance. Received genException: (%s)", strerror ( errno ) );
        std::cout << "Abnormal Termination\n" ;
  }
  return NULL; // hi lint
}

std::string  compp::SSrv::executeTransaction (  const std::string & request, SSrvContext & Context ) { 
  std::string ret = "SSRvTest";
  ret += Context.GetContextString();
  ret += "HOWDI";
  return ret ;
} 
